import datetime

import coin.exchange.base.kr_rest.level_book as level_book_md
import coin.exchange.base.kr_rest.public_client_base as pubcb
from coin.exchange.base.kr_rest.rest_client_base import RestUpdate
import coin.exchange.upbit_v1.kr_rest.native_public_client as npubc
from coin.exchange.upbit_v1.kr_rest.product import (UpbitProduct, get_active_symbol_list)
from coin.exchange.upbit_v1.kr_rest.constants import coin_list

from coin.proto.coin_market_query_pb2 import (
    ProductTicker,
    ExchangeTicker,
    ProductKlineElement,
    ProductKline,
)


class UpbitFeedParser(object):
  @staticmethod
  def parse_native_ticker(update_msg, product):
    # <RestUpdate 1531190154.9743047 1531190155.9229712 [{'market': 'KRW-BTC', 'trade_date': '20180710', 'trade_time': '023553', 'trade_date_kst': '20180710', 'trade_time_kst': '113553', 'trade_timestamp': 1531190153000, 'opening_price': 7474000.0, 'high_price': 7518000.0, 'low_price': 7450000.0, 'trade_price': 7459000.0, 'prev_closing_price': 7474000.0, 'change': 'FALL', 'change_price': 15000.0, 'change_rate': 0.0020069575, 'signed_change_price': -15000.0, 'signed_change_rate': -0.0020069575, 'trade_volume': 0.01344575, 'acc_trade_price': 4009749520.04717, 'acc_trade_price_24h': 29286327789.01655, 'acc_trade_volume': 536.03289853, 'acc_trade_volume_24h': 3900.73302478, 'highest_52_week_price': 28885000.0, 'highest_52_week_date': '2018-01-06', 'lowest_52_week_price': 4175000.0, 'lowest_52_week_date': '2017-09-25', 'timestamp': 1531190158168}] ==>
    assert type(update_msg) == list
    assert len(update_msg) == 1
    update_msg = update_msg[0]
    return ProductTicker(high=float(update_msg['high_price']),
                         low=float(update_msg['low_price']),
                         last=float(update_msg['trade_price']),
                         exchange_timestamp=int(float(update_msg['trade_timestamp']) * (10**6)),
                         volume_24hr=float(update_msg['acc_trade_volume']),
                         symbol=product.symbol,
                         native_symbol=product.native_symbol)

  @staticmethod
  def parse_exchange_ticker(update_msg):
    product_ticker_list = []
    for msg in update_msg:
      native_symbol = msg['market']
      product = UpbitProduct.FromStrNativeProductNothrow(native_symbol)
      if product is None:
        continue
      product_ticker_list.append(UpbitFeedParser.parse_native_ticker([msg], product))

    return ExchangeTicker(exchange="Upbit", each_ticker=product_ticker_list)

  @staticmethod
  def parse_native_kline(update_msg, product, kline_period, start_time, end_time):
    klines = update_msg
    kline_group = []
    for kline in klines:
      kline_timestamp = datetime.datetime.strptime(kline['candle_date_time_utc'],
                                                   "%Y-%m-%dT%H:%M:%S").timestamp()
      if kline_timestamp >= start_time.timestamp() and \
        kline_timestamp < end_time.timestamp():
        product_kline = ProductKlineElement(
            kline_timestamp=int(kline_timestamp * 1e+9),
            open=float(kline['opening_price']),
            high=float(kline['high_price']),
            low=float(kline['low_price']),
            close=float(kline['trade_price']),
            volume=float(kline['candle_acc_trade_volume']),
        )
        kline_group.append(product_kline)
    assert len(kline_group) > 0
    return ProductKline(
        symbol=product.symbol,
        native_symbol=product.native_symbol,
        exchange='Upbit',
        market_type='Spot',
        kline_period=kline_period,
        klines=kline_group,
    )


class UpbitPublicClient(pubcb.PublicClientBase):
  def __init__(self, timeout=None):
    self.npubc = npubc.UpbitNativePublicClient()

  def query_ticker_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.query_ticker(symbol)
    update.msg = UpbitFeedParser.parse_native_ticker(update.msg, product)
    return update

  def query_exchange_ticker_impl(self):
    symbol_list = get_active_symbol_list()
    update = self.npubc.query_ticker(','.join(symbol_list))
    update.msg = UpbitFeedParser.parse_exchange_ticker(update.msg)
    return update

  def query_history_kline_impl(self, product, kline_type, start_time, end_time):
    symbol = product.native_symbol
    kline_period = kline_type[:-1]
    kline_unit = kline_type[-1]
    size = 1000
    if kline_unit == 'm':
      update = self.npubc.get_history_kline_minite(symbol, int(kline_period), size, end_time)
    elif kline_unit == 'd':
      assert kline_period == '1', ("Invalid kline_type", kline_type)
      update = self.npubc.get_history_kline_day(symbol, size, end_time)
    else:
      raise NotImplementedError()
    update.msg = UpbitFeedParser.parse_native_kline(update.msg,
                                                    product,
                                                    kline_type,
                                                    start_time,
                                                    end_time)
    return update

  def query_level_book_impl(self, product):
    update = self.npubc.query_market_all()
    update.msg = level_book_mdl.LevelBook(product)
    return update
